#include "pch.hpp"
#include "D3D11SamplerState.hpp"
#include "D3D11Exception.hpp"

using namespace Engine;
using namespace Engine::Video;




D3D11SamplerState::D3D11SamplerState(const notify& destroyedFn, const ID3D11DevicePtr& device)
	: m_destroyed(destroyedFn)
	, m_device(device)

	, m_changed(true)

	, m_filterType(Filter::MinMagMipLinear)
	, m_addressU(Address::Clamp)
	, m_addressV(Address::Clamp)
	, m_addressW(Address::Clamp)
	, m_minLOD(std::numeric_limits<float>::min())
	, m_maxLOD(std::numeric_limits<float>::max())
	, m_mipLODBias(0)
	, m_maxAnisotropy(16)
	, m_comparisonFunc(Comparison::Never)
{}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::post_create(ptr _this)
{
	m_this = _this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11SamplerState::~D3D11SamplerState()
{
	m_destroyed(m_this);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




const ID3D11SamplerStatePtr& D3D11SamplerState::apply()
{
	updateState();
	return m_samplerState;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void D3D11SamplerState::setFilter(Filter::Type filter)
{
	m_filterType = filter;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setAddressU(Address::Type address)
{
	m_addressU = address;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setAddressV(Address::Type address)
{
	m_addressV = address;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setAddressW(Address::Type address)
{
	m_addressW = address;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setMipLODBias(float bias)
{
	m_mipLODBias = bias;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setMaxAnisotropy(uint32 max)
{
	if(max < 1 || max > 16)
		BOOSTEXT_THROW(invalid_value_exception(max));

	m_maxAnisotropy = max;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setComparisonFunc(Comparison::Type func)
{
	m_comparisonFunc = func;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setMinLOD(float min)
{
	m_minLOD = min;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setMaxLOD(float max)
{
	m_maxLOD = max;
	m_changed = true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void D3D11SamplerState::setBorderColor(const float4& color)
{
	m_borderColor = color;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




Filter::Type D3D11SamplerState::filter() const
{
	return m_filterType;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Address::Type D3D11SamplerState::addressU() const
{
	return m_addressU;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Address::Type D3D11SamplerState::addressV() const
{
	return m_addressV;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Address::Type D3D11SamplerState::addressW() const
{
	return m_addressW;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float D3D11SamplerState::mipLODBias() const
{
	return m_mipLODBias;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

uint32 D3D11SamplerState::maxAnisotropy() const
{
	return m_maxAnisotropy;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Comparison::Type D3D11SamplerState::comparisonFunc() const
{
	return m_comparisonFunc;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float D3D11SamplerState::minLOD() const
{
	return m_minLOD;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float D3D11SamplerState::maxLOD() const
{
	return m_maxLOD;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4 D3D11SamplerState::borderColor() const
{
	return m_borderColor;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void D3D11SamplerState::updateState()
{
	if(!m_changed)
		return;

	D3D11_SAMPLER_DESC desc = createDesc();
	ID3D11SamplerState* state = NULL;
	D3D11_API(m_device->CreateSamplerState(&desc, &state));
	m_samplerState = ID3D11SamplerStatePtr(state);

	// Now we are allowed to change the flag: before we were not
	// because CreateSamplerState might throw an exception
	m_changed = false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11_SAMPLER_DESC D3D11SamplerState::createDesc() const
{
	D3D11_SAMPLER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));

	desc.Filter         = filter(m_filterType);
	desc.AddressU       = Address(m_addressU);
	desc.AddressV       = Address(m_addressV);
	desc.AddressW       = Address(m_addressW);
	desc.MinLOD         = m_minLOD;
	desc.MaxLOD         = m_maxLOD;
	desc.MipLODBias     = m_mipLODBias;
	desc.MaxAnisotropy  = m_maxAnisotropy;
	desc.ComparisonFunc = comparisonFunc(m_comparisonFunc);
	desc.BorderColor[0] = m_borderColor.x;
	desc.BorderColor[1] = m_borderColor.y;
	desc.BorderColor[2] = m_borderColor.z;
	desc.BorderColor[3] = m_borderColor.w;

	return desc;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11_FILTER D3D11SamplerState::filter(Filter::Type filter)
{
	switch(filter)
	{
	case Filter::MinMagMipPoint: return D3D11_FILTER_MIN_MAG_MIP_POINT;
	case Filter::MinMagPointMipLinear: return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
	case Filter::MinPointMagLinearMipPoint: return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
	case Filter::MinPointMagMipLinear: return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
	case Filter::MinLinearMagMipPoint: return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
	case Filter::MinLinearMagPointMipLinear: return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
	case Filter::MinMagLinearMipPoint: return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
	case Filter::MinMagMipLinear: return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	case Filter::Anisotropic: return D3D11_FILTER_ANISOTROPIC;
	default: BOOSTEXT_THROW(invalid_value_exception(filter));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11_TEXTURE_ADDRESS_MODE D3D11SamplerState::Address(Address::Type address)
{
	switch(address)
	{
	case Address::Wrap: return D3D11_TEXTURE_ADDRESS_WRAP;
	case Address::Mirror: return D3D11_TEXTURE_ADDRESS_MIRROR;
	case Address::Clamp: return D3D11_TEXTURE_ADDRESS_CLAMP;
	case Address::Border: return D3D11_TEXTURE_ADDRESS_BORDER;
	case Address::MirrorOnce: return D3D11_TEXTURE_ADDRESS_MIRROR_ONCE;
	default: BOOSTEXT_THROW(invalid_value_exception(address));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

D3D11_COMPARISON_FUNC D3D11SamplerState::comparisonFunc(Comparison::Type func)
{
	switch(func)
	{
	case Comparison::Never: return D3D11_COMPARISON_NEVER;
	case Comparison::Less: return D3D11_COMPARISON_LESS;
	case Comparison::Equal: return D3D11_COMPARISON_EQUAL;
	case Comparison::LessEqual: return D3D11_COMPARISON_LESS_EQUAL;
	case Comparison::Greater: return D3D11_COMPARISON_GREATER;
	case Comparison::NotEqual: return D3D11_COMPARISON_NOT_EQUAL;
	case Comparison::GreaterEqual: return D3D11_COMPARISON_GREATER_EQUAL;
	case Comparison::Always: return D3D11_COMPARISON_ALWAYS;
	default: BOOSTEXT_THROW(invalid_value_exception(func));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
